package com.ssky.bean.validator;

import com.ssky.bean.validator.core.*;
import com.ssky.bean.validator.core.checker.AbstractChecker;
import com.ssky.bean.validator.core.checker.IChecker;
import com.ssky.bean.validator.core.checker.MethodChecker;
import com.ssky.bean.validator.core.exception.VldException;
import com.ssky.bean.validator.core.rule.Field;
import com.ssky.bean.validator.core.rule.Property;
import com.ssky.bean.validator.core.rule.Rule;
import com.ssky.bean.validator.core.rule.ValidatorRule;
import com.ssky.bean.validator.factory.RuleCheckerFactory;
import com.ssky.bean.validator.proxy.*;
import com.ssky.bean.validator.util.DefinerUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: ssky
 * @Date: 2021/07/17
 * @Description: <li>1. 已知一个对象的规则实体对象
 * <li>2. 构建实体对象的fieldValidator和定制的methodChecker
 * <li>3. 对限定规则进行检查：list中依赖不能被外部属性对象依赖
 * <li>4. 在构建过程中，还将通过动态字节码技术创建取值器、规则方法（脚本）处理的实现类（考虑用反射或者一些表达式取值有性能消耗，
 * 所以在创建过程通过字节码对每一个属性取值生成取值处理器java类，以达到空间换时间的目的），并注入到FeildValidator和checker中。
 * <li>5.
 * <li>6.
 * <li>7.
 * <li>8.
 */
public class ValidatorBuilder {
    private static final Logger L = LoggerFactory.getLogger(ValidatorBuilder.class);

    private Class validateObjectClazz;

    //<ruleId, checker>
    private final Map<String, IChecker> checkerMap = new HashMap<>();

    //<fieldFullname,fullFieldValueGetter>
    private Map<String, IFullFieldValueGetter> fullFieldValueGetterMap;

    // <  class --<fieldName,IFullFieldValueGetter>   >
    private Map<Class, Map<String, IFieldValueGetter>> classFieldValueGetterMap;

    private final VldConditionHanlder vldConditionHanlder = new VldConditionHanlder(checkerMap);


    public FieldValidator<?, ?> build(ValidatorRule validatorRule) throws Exception {
        String validatorId = validatorRule.getId();
        L.info("validator building--------->validatorId：" + validatorRule.getId() + "; validator chassType=" + validatorRule.getClassName());

        this.validateObjectClazz = Class.forName(validatorRule.getClassName());

        FullFieldValueGetterCreator fullFieldValueGetterCreator = new FullFieldValueGetterCreator();
        fullFieldValueGetterMap = fullFieldValueGetterCreator.createAll(validateObjectClazz);

        FieldValueGetterCreator fieldValueGetterCreator = new FieldValueGetterCreator();
        classFieldValueGetterMap = fieldValueGetterCreator.createAll(validateObjectClazz);

        Field beanField = new Field();//beanClass也构建为一个field
        beanField.setClassName(validatorRule.getClassName());
        beanField.setClassType(validateObjectClazz);
        beanField.setFbTag(null);
        beanField.setFullName(null);
        beanField.setBeanClassType(validateObjectClazz);
        beanField.setPreField(null);

        List<Field> fields = validatorRule.getFields();
        if (fields != null) {
            for (Field firstField : fields) {
                firstField.setPreField(beanField);
            }
        }

        FieldValidator<?, ?> fieldValidator = new FieldValidator<>(beanField);////beanClass也构建为一个FieldValidator
        fieldValidator.setFieldValueGetter(null);//bean自身不涉及取值
        if (validatorRule.getMethodRules() != null) {
            List<IChecker<?, ?, ?>> checkers = bulidBeanMethodChecker(validatorRule.getMethodRules(), beanField);
            fieldValidator.addCheckers(checkers);
        }

        if (fields != null) {
            bulidFieldValidateor(fieldValidator, fields);
        }

        for (String key : this.checkerMap.keySet()) {
            IChecker checker = checkerMap.get(key);
            checker.init();
        }
        return fieldValidator;
    }

    private List<IChecker<?, ?, ?>> bulidBeanMethodChecker(List<Rule> methodRules, Field beanField) throws Exception {
        List<IChecker<?, ?, ?>> checkerLs = new ArrayList<>();
        for (Rule rule : methodRules) {
            Class<?> checkerClass = RuleCheckerFactory.getCheckerClass(rule.getType());
            if (checkerClass == null) {
                throw new VldException("err: ruleId=" + rule.getId() + "; not found  checker \"" + rule.getType() + "\"");
            }
            MethodCheckerHandlerCreator creator = new MethodCheckerHandlerCreator();
            IMethodCheckerHandler methodCheckerHandler = creator.create(rule.getId(), rule.getMImport(), rule.getMethodBody());
            MethodChecker mc = (MethodChecker) checkerClass.newInstance();
            mc.setField(beanField);
            mc.setRule(rule);
            mc.setMethodCheckerHandler(methodCheckerHandler);

            if (!StringUtils.isBlank(rule.getAllDepExp())) {
                DepExpExeHandlerCreator depExpExeHandlerCreator = new DepExpExeHandlerCreator();
                AbstractDepExpExeHandler depExpExeHandler = depExpExeHandlerCreator.create(rule, vldConditionHanlder);
                mc.setDepExpExeHandler(depExpExeHandler);
            }
        }
        return checkerLs;
    }

    private void bulidFieldValidateor(FieldValidator previousValidator, List<Field> fields) throws Exception {

        for (Field field : fields) {
            Field preField = field.getPreField();
            Class preFieldClass = preField.getClassType();

            FieldValidator<?, ?> fieldValidator = new FieldValidator<>(field);

            if (DefinerUtils.typeIsSupportGet(preField)) {
                Map<String, IFieldValueGetter> fvgMap = classFieldValueGetterMap.get(preFieldClass);
                if (fvgMap != null) {
                    IFieldValueGetter fvGetter = fvgMap.get(field.getName());
                    fieldValidator.setFieldValueGetter(fvGetter);
                }
            }

            // localField thisField
            Map<String, IFieldValueGetter> localFieldValueGetterMap = classFieldValueGetterMap.get(preFieldClass);
            Map<String, IFieldValueGetter> thisFieldValueGetterMap = classFieldValueGetterMap.get(field.getClassType());
            FieldValueGetHandler fieldValueGetHandler = new FieldValueGetHandler(this.fullFieldValueGetterMap, localFieldValueGetterMap, thisFieldValueGetterMap);
            List<Rule> rules = field.getRules();
            if (rules != null) {
                List<IChecker<?, ?, ?>> checkers = bulidChecker(fieldValidator, rules, fieldValueGetHandler);
                fieldValidator.addCheckers(checkers);
            }

            List<Field> innerFields = field.getFields();
            if (innerFields != null) {
                bulidFieldValidateor(fieldValidator, innerFields);
            }
            previousValidator.addFieldValidator(fieldValidator);
        }
    }


    private List<IChecker<?, ?, ?>> bulidChecker(FieldValidator<?, ?> fieldValidator, List<Rule> rules, IFieldValueGetHandler fieldValueGetHandler) throws Exception {
        Field field = fieldValidator.getField();
        //注入IFieldValueGetter、IFullFieldValueGetter
        List<IChecker<?, ?, ?>> checkerLs = new ArrayList<>();
        for (Rule rule : rules) {
            Class<AbstractChecker> checkerClazz = RuleCheckerFactory.getCheckerClass(rule.getType());
            if (checkerClazz == null) {
                throw new VldException("err: ruleId=" + rule.getId() + "; checker'" + rule.getType() + "' not found！");
            }

            AbstractChecker checker = checkerClazz.newInstance();
            checker.setField(field);
            checker.setRule(rule);
            checker.setFieldValueGetHandler(fieldValueGetHandler);
            if (!StringUtils.isBlank(rule.getAllDepExp())) {
                DepExpExeHandlerCreator depExpExeHandlerCreator = new DepExpExeHandlerCreator();
                AbstractDepExpExeHandler depExpExeHandler = depExpExeHandlerCreator.create(rule, vldConditionHanlder);
                checker.setDepExpExeHandler(depExpExeHandler);
            }
            if (VldConstants.RuleType_Method.equals(rule.getType())) {
                if (rule.getMethodBody() == null || rule.getMethodBody().trim().equals("")) {
                    throw new VldException("ValidatorClass:" + field.getBeanClassType() + "; ruleId:" + rule.getId() + "; tags <method> is not empty!");
                }
                MethodCheckerHandlerCreator creator = new MethodCheckerHandlerCreator();
                IMethodCheckerHandler methodCheckerHandler = creator.create(rule.getId(), rule.getMImport(), rule.getMethodBody());
                MethodChecker mc = (MethodChecker) checker;
                mc.setMethodCheckerHandler(methodCheckerHandler);
            } else {
                List<Property> propertys = rule.getPropertys();
                if (null != propertys) {
                    for (Property proty : propertys) {
                        try {
                            Method m = checkerClazz.getMethod("set" + DefinerUtils.firstCharUpper(proty.getName()), new Class[]{String.class});

                            m.invoke(checker, new Object[]{proty.getValue()});
                        } catch (Exception e) {
                            L.error("ruleId=" + rule.getId(), e);
                            throw e;
                        }
                    }
                }
            }
            if (DefinerUtils.isChecker(rule)) {
                checkerLs.add(checker);
            }
            this.checkerMap.put(rule.getId(), (IChecker) checker);
        }
        return checkerLs;
    }
}
